#include <iostream>
#include <string>
#include <cstring>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define SIZE 1024

// 用户聊天室--client端有两个功能:
// 1.发送数据
// 2.获取其他用户发送的消息(获取服务端的信息)
struct TheradData
{
    int sockfd_;
    sockaddr_in server_;

    TheradData(int &sockfd, sockaddr_in &server)
        :sockfd_(sockfd),server_(server)
    {}
};
// 给 服务端 发送数据信息
void *Send_Rountine(void* args)
{
    TheradData *td = static_cast<TheradData*>(args);
    std::string message;
    while (true)
    {
        std::cout << "Please Enter@ ";
        getline(std::cin, message);
        // 发送数据
        sendto(td->sockfd_, message.c_str(), message.size(), 0, (const sockaddr*)&(td->server_), sizeof(td->server_));
    }
}
// 获取服务端返回的信息(获取其他用户发送的数据信息)
void *Recv_Rountine(void* args)
{
    TheradData *td = static_cast<TheradData*>(args);
    char buffer[SIZE*4];
    while (true)
    {
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        // 接收服务端数据
        ssize_t s = recvfrom(td->sockfd_, buffer, sizeof(buffer), 0, (sockaddr*)&temp, &len);
        if(s > 0)
        {
            buffer[s] = 0;
            std::cerr << buffer << std::endl;
        }
    }
    
}

void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./udpclient server_ip server_port
int main(int argc, char *argv[])
{
    if(argc != 3){
        Usage(argv[0]);
        exit(0);
    }
    std::string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    // 开始向 Udp 服务端发送数据信息
    // 只要进行 网络通信 就需要创建 socket 套接字
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    
    // 1.创建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0){
        std::cout << "Create Socket Error..." << std::endl;
        return 1;
    }
    
    // client 要bind！只不过不需要用户显示的bind！一般有OS自由随机选择！
    // 一个端口号只能被一个进程bind，对server是如此，对于client，也是如此！
    // 其实client的port是多少，其实不重要，只要能保证主机上的唯一性就可以！
    // 系统首次发送数据的时候给我bind

    // 2.直接发送数据
    // std::string message;
    // char buffer[SIZE];
    // while (true)
    // {
    //     message.clear();
    //     bzero(buffer, sizeof(buffer));
    //     std::cout << "Please Enter@ ";
    //     getline(std::cin, message);
    //     // 发送数据
    //     sendto(sockfd, message.c_str(), message.size(), 0, (const sockaddr*)&server, sizeof(server));
        
    //     // 发送数据到 server 完毕，服务器会立即为我处理这些数据，这个速度是非常快的
    //     // 即，可以直接从 服务器上 拿取服务器为我们处理好的数据 了
    //     struct sockaddr_in temp;
    //     socklen_t len = sizeof(temp);
    //     // 拿取服务器为我们处理好的数据
    //     ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (sockaddr*)&temp, &len);
    //     if(s > 0){
    //         buffer[s] = 0;
    //         // std::cout << "Recv Server Send message: " << buffer << std::endl;
    //         std::cout << buffer << std::endl;
    //     }
    // }

    // 用户聊天室
    // 创建两个线程,一个获取用户输入向 server 发送，一个接收 server 端消息
    pthread_t send, recv;
    TheradData *threaddata = new TheradData(sockfd, server);
    
    pthread_create(&send, nullptr, Send_Rountine, threaddata);
    pthread_create(&recv, nullptr, Recv_Rountine, threaddata);
    
    pthread_join(send, nullptr);
    pthread_join(recv, nullptr);

    close(sockfd);
    return 0;
}